#include "indextable.h"
#include"buffermanager.h"
#include "SysException.h"
#include "block.h"
#include <map>
#include <string>
#include <memory>
using namespace std;

IndexTable::IndexTable(shared_ptr<BufferManager> buffermanager, const std::string &table_name) 
	:buffermanager(buffermanager), table_name(table_name), empty_table(empty_table_size, false)
{
	try{
		first_block = buffermanager->readBlock(table_name + ".ind", 0);
	}
	catch (CSysException e)
	{
		if (e.getErrorNum() == FILE_NOT_FOUNT_ERROR)
			buffermanager->writeBlock(table_name + ".ind", 0, first_block);
	}
	
	//read in the blocks status
	for (int i = BLOCK_SIZE - 1; i >= BLOCK_SIZE - empty_table_size; --i)
	{
		empty_table[BLOCK_SIZE - i - 1] = first_block.block[i] != 0;
	}
	unsigned int offset = 0;
	string index_name;
	string attr_name;
	string type;
	byte length;
	byte head_block;
	byte index_block;
	while (true)
	{
		if (first_block.block[offset] == 0)
			break;
		if (first_block.block[offset] == 0xE5)
		{
			offset += index_info_length;
			continue;
		}
		index_name = first_block.readChar(offset, index_name_mlen);
		offset += index_name_mlen;
		attr_name = first_block.readChar(offset, attr_name_mlen);
		offset += attr_name_mlen;
		type = first_block.readChar(offset, type_mlen);
		offset += type_mlen;
		length = first_block.block[offset++];
		head_block = first_block.readInt(offset); offset += 4;
		index_block = first_block.readInt(offset); offset += 4;
		tree_table[attr_name] = new BplusTree((table_name + ".ind"), type, length, index_name, 
			attr_name, head_block, index_block, buffermanager, empty_table, true);
	}
}

IndexTable::~IndexTable()
{
	int offset = 0;
	while (true)
	{
		if (first_block.block[offset] == 0x00)
			break;
		if (first_block.block[offset] == 0xE5)
		{
			offset += index_info_length;
			string attr_name = first_block.readChar(offset, attr_name_mlen);
			auto it = tree_table.find(attr_name);
			if (it != tree_table.end())
				tree_table.erase(it);
			continue;
		}
		offset += index_name_mlen;
		string attr_name = first_block.readChar(offset, attr_name_mlen);
		int head_block = tree_table[attr_name]->getHeadBlock();
		offset += attr_name_mlen + type_mlen + index_info_length + sizeof(byte);
		first_block.writeInt(offset, head_block);
		delete tree_table[attr_name];
		auto it = tree_table.find(attr_name);
		if (it != tree_table.end())
			tree_table.erase(it);
	}
	assert(tree_table.size() == 0);
	for (int i = BLOCK_SIZE - 1; i >= BLOCK_SIZE - empty_table_size; --i)
	{
		first_block.block[i] = empty_table[BLOCK_SIZE - i - 1];
	}
	buffermanager->writeBlock(table_name + ".ind", 0, first_block);
}

void IndexTable::createIndex(string index_name, string attr_name, string type, byte len)
{
	if (tree_table[attr_name] != NULL)
		return;//has set up an index on attr_name
	int head_block = distance(empty_table.cbegin(), find(empty_table.cbegin(),
		empty_table.cend(), false));
	empty_table[head_block] = true;
	int index_block = distance(empty_table.cbegin(), find(empty_table.cbegin(),
		empty_table.cend(), false));
	empty_table[head_block] = true;
	BplusTree *tree = new BplusTree(table_name + ".ind", type, len,
		index_name, attr_name, head_block, index_block, buffermanager, empty_table, false);
	tree_table[attr_name] = tree;
	int offset = 0;
	while (true)
	{
		if (first_block.block[offset] == 0 || first_block.block[offset] == 0xE5)
			break;
		offset += index_info_length;
	}
	first_block.writeChar(tree->getIndexName(), offset);
	offset += index_name_mlen;
	first_block.writeChar(tree->getAttrName(), offset);
	offset += attr_name_mlen;
	first_block.writeChar(tree->getType(), offset);
	offset += type_mlen;
	first_block.block[offset++] = tree->getLen();
	first_block.writeInt(offset, tree->getHeadBlock());
	offset += 4;
	first_block.writeInt(offset, tree->getIndexBlock());
	offset += 4;
}

void IndexTable::dropIndex(string index_name)
{
	auto it = tree_table.cbegin();
	BplusTree *tree;
	for (; it != tree_table.cend(); ++it)
	{
		tree = it->second;
		if (tree->getIndexName() == index_name)
		{
			tree->DropTree();
			delete tree;
			break;
		}
	}
	if (it == tree_table.cend())
		throw CSysException("no index " + index_name + " exists");
	int offset = 0;
	while (true)
	{
		if (index_name == first_block.readChar(offset, index_name_mlen))
		{
			first_block.block[offset] = 0xE5;
			break;
		}
		offset += index_info_length;
	}
}